home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / crh_n.arc / NANSI.ASM < prev    next >
Assembly Source File  |  1990-03-09  |  30KB  |  1,129 lines

  1.     page    66, 132
  2. ;--- nansi.asm ----------------------------------------------------------
  3. ; New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;    nansi.asm   - all DOS function handlers except init
  9. ;    nansi_p.asm - parameter parser for ANSI escape sequences
  10. ;    nansi_f.asm - ANSI command handlers
  11. ;    nansi_i.asm - init DOS function handler
  12. ;
  13. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  14. ; Revision history:
  15. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  16. ; 6  july 85: split off ANSI stuff into other files, added backspace
  17. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  18. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  19. ; 9  aug 85:  added cursor position reporting
  20. ; 10 aug 85:  added output character translation
  21. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  22. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  23. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  24. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  25. ;          direct cursor positioning, takeover of BIOS write_tty,
  26. ;          noticed & squashed 2 related bugs in tab expansion
  27. ; 13 feb 86:  Squashed them again, harder
  28. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  29. ;          If the addition of the beep period to the
  30. ;          BIOS low timer word results in an overflow, the beep will be
  31. ;          supressed. Also made code compatible eith earlier versions
  32. ;          of assembler.
  33. ; 22 feb 90:  modified to skip nulls  C.R. Henderson              (CRH)
  34. ; 23 feb 90:  modified to perform a non-overwrite TAB expansion        (CRH)
  35. ;  9 mar 90:  modified to stuff 'nansi' followed by a carriage return    (CRH)
  36. ;             into the keyboard buffer in response to a ESC[c command
  37. ;------------------------------------------------------------------------
  38.  
  39.     include nansi_d.asm    ; definitions
  40.  
  41.     ; from nansi_f.asm
  42.     extrn    f_escape:near, f_in_escape:near
  43.  
  44.     ; from nansi_p.asm
  45.     extrn    param_end:word, redef_end:word
  46.  
  47.     ; from nansi_i.asm
  48.     extrn    dosfn0:near
  49.  
  50.     ; to nansi_p.asm
  51.     public    f_loopdone
  52.     public    f_not_ansi
  53.     public    f_ansi_exit
  54.  
  55.     ; to both nansi_p.asm and nansi_f.asm
  56.     public    cur_x, cur_y, max_x, cur_attrib
  57.  
  58.     ; to nansi_f.asm
  59.     public    xy_to_regs, get_blank_attrib
  60.     public    port_6845
  61.     public    wrap_flag
  62.     public    cur_parm_ptr
  63.     public    cur_coords, saved_coords, max_y
  64.     public    escvector, string_term
  65.     public    cpr_esc, cprseq
  66.     public    id_str, id_end, idseq            ; CRH 3-9-90
  67.     public    video_mode
  68.     public    lookup
  69.     public    in_g_mode
  70.  
  71.     ; to nansi_i.asm
  72.     public    req_ptr, break_handler
  73.     public    int_29
  74.     if    takeBIOS
  75.     public    new_vid_bios, old_vid_bios
  76.     endif
  77.  
  78.     ; to all modules
  79.     public    xlate_tab_ptr
  80.  
  81. ;--- seg_cs is the CS: override prefix
  82. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  83. seg_cs    macro
  84.     db    2eh
  85.     endm
  86.  
  87. ;--- push_all, pop_all ------------------------------------------------
  88. ; Save/restore all user registers.
  89. push_all    macro
  90.     if    is_8088
  91.     push    ax
  92.     push    bx
  93.     push    cx
  94.     push    dx
  95.     push    bp
  96.     push    si
  97.     push    di
  98.     else
  99.     pusha
  100.     endif
  101.     endm
  102.  
  103. pop_all macro
  104.     if    is_8088
  105.     pop    di
  106.     pop    si
  107.     pop    bp
  108.     pop    dx
  109.     pop    cx
  110.     pop    bx
  111.     pop    ax
  112.     else
  113.     popa
  114.     endif
  115.     endm
  116.  
  117. keybuf    struc                ; Used in getchar
  118. len    dw    ?
  119. adr    dw    ?
  120. keybuf    ends
  121.  
  122.  
  123. ABS40    segment at 40h
  124.     org    1ah
  125. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  126. buffer_tail    dw    ?
  127.  
  128.     org    49h
  129. crt_mode    db    ?
  130. crt_cols    dw    ?
  131. crt_len        dw    ?
  132. crt_start    dw    ?
  133. cursor_posn    dw    8 dup (?)
  134. cursor_mode    dw    ?
  135. active_page    db    ?
  136. addr_6845    dw    ?
  137. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  138. crt_palette    db    ?
  139.     org    6ch
  140. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  141.  
  142. ABS40    ends
  143.  
  144.     page
  145.  
  146. CODE    segment byte public 'CODE'
  147. assume    cs:code, ds:code
  148.  
  149.     ; Device Driver Header
  150.  
  151.     org    0
  152.  
  153.     dd    -1            ; next device
  154.     dw    8013h            ; attributes
  155.     dw    strategy        ; request header pointer entry
  156.     dw    interrupt        ; request entry point
  157.     db    'CON'            ; device name (8 char)
  158.     db    5 dup (20h)        ;  ... and 5 blanks)
  159.  
  160.     ; Identification- in case somebody TYPEs the assembled driver
  161.     db    27,'[2J'
  162.     db    "Nansi.sys v2.2"
  163.     ife    is_8088
  164.     db    "(80286)"
  165.     endif
  166.     db    ': New ANSI driver (C) Daniel Kegel, Pasadena, CA 1986'
  167.     db    13, 10, 26
  168.  
  169.  
  170. ;----- variable area --------------------
  171. req_ptr label    dword
  172. req_off dw    ?
  173. req_seg dw    ?
  174.  
  175. wrap_flag    db    1    ; 0 = no wrap past line end
  176. escvector    dw    0    ; state vector of ESCape sequencor
  177. video_mode    db    3    ; ROM BIOS video mode (2=BW, 3=color)
  178. max_y        db    24
  179. max_cur_x    label    word    ; used to get both max & cur at once
  180. max_x        db    79    ; line width (79 for 80x25 modes)
  181. cur_coords    label    word
  182. cur_x        db    0    ; cursor position (0 = left edge)
  183. cur_y        db    0    ;          (0 = top edge)
  184. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  185. string_term    db    0    ; either escape or double quote
  186. cur_attrib    db    7    ; current char attributes
  187. cur_page    db    0    ; current display page
  188. video_seg    dw    ?    ; segment of video card
  189. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  190. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  191. port_6845    dw    ?    ; port address of 6845 card
  192. xlate_tab_ptr    dw    ?    ; pointer to output translation table
  193.         if    takeBIOS
  194. old_vid_bios    dd    ?    ; pointer to old video bios routine
  195.         endif
  196.  
  197. brkkeybuf    db    3    ; control C
  198. fnkeybuf    db    ?    ; holds second byte of fn key codes
  199. cpr_buf        db    8 dup (?), '['
  200. cpr_esc        db    1bh    ; descending buffer for cpr function
  201. id_str        db    13,"isna"
  202. id_end        db    'n'
  203.  
  204. ; following four keybufs hold information about input
  205. ; Storage order determines priority- since the characters making up a function
  206. ; key code must never be separated (say, by a Control-Break), they have the
  207. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  208. ; lowest priority.
  209.  
  210. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  211. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  212. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  213. xlatseq keybuf    <0>        ; keyboard reassignment string
  214. idseq    keybuf    <0>        ; response to ESC [ c          CRH 3-9-90
  215.  
  216. ;------ xy_to_regs --------------------------------------------
  217. ; on entry: x in cur_x, y in cur_y
  218. ; on exit:  dx = chars left on line, di = address
  219. ; Alters ax, bx.
  220. xy_to_regs    proc    near
  221.     ; Find number of chars 'till end of line, keep in DX
  222.     mov    ax, max_cur_x
  223.     mov    bx, ax            ; save max_x & cur_x for next block
  224.     mov    ah, 0            ; ax = max_x
  225.     xchg    dx, ax
  226.     mov    al, bh
  227.     mov    ah, 0            ; ax = cur_x
  228.     sub    dx, ax
  229.     inc    dx            ; dx is # of chars till EOL
  230.     ; Calculate DI = current address in text buffer
  231.     mov    al, bl            ; al = max_x
  232.     inc    al
  233.     mul    cur_y
  234.     add    al, bh            ; al += cur_x
  235.     adc    ah, 0            ; AX is # of chars into buffer
  236.     add    ax, ax
  237.     xchg    di, ax            ; DI is now offset of cursor.
  238.     ret
  239. xy_to_regs    endp
  240.  
  241.  
  242. ;------- dos_fn_tab -------------
  243. ; This table is used in "interrupt" to call the routine that handles
  244. ; the requested function.
  245.  
  246. max_cmd equ    12
  247. dos_fn_tab:
  248.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  249.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  250.  
  251. ;------- strategy ----------------------------------------------------
  252. ; DOS calls strategy with a request which is to be executed later.
  253. ; Strategy just saves the request.
  254.  
  255. strategy    proc    far
  256.     mov    cs:req_off,BX
  257.     mov    cs:req_seg,ES
  258.     ret
  259. strategy    endp
  260.  
  261. ;------ interrupt -----------------------------------------------------
  262. ; This is where the request handed us during "strategy" is
  263. ; actually carried out.
  264. ; Calls one of 12 subroutines depending on the function requested.
  265. ; Each subroutine returns with exit status in AX.
  266.  
  267. interrupt    proc    far
  268.     sti
  269.     push_all            ; preserve caller's registers
  270.     push    ds
  271.     push    es
  272.  
  273.     ; Read requested function information into registers
  274.     lds    bx,cs:req_ptr
  275.     mov    al,[BX+02h]        ; al = function code
  276. ;
  277. ; The next instruction blows up MASM 1.0 but who cares!!
  278. ;
  279.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  280.     mov    cx,[BX+12h]        ; cx = input/output byte count
  281.  
  282.     cmp    al, max_cmd
  283.     ja    unk_command        ; too big, exit with error code
  284.  
  285.     xchg    bx, ax
  286.     shl    bx, 1            ; form index to table of words
  287.     mov    ax, cs
  288.     mov    ds, ax
  289.     call    word ptr dos_fn_tab[bx]
  290. int_done:
  291.     lds    bx,cs:req_ptr        ; report status
  292.     or    ax, 100h        ; (always set done bit upon exit)
  293.     mov    [bx+03],ax
  294.  
  295.     pop    ES            ; restore caller's registers
  296.     pop    DS
  297.     pop_all
  298.     ret                ; return to DOS.
  299.  
  300. unk_command:
  301.     call    badcmd
  302.     jmp    int_done
  303.  
  304. interrupt    endp
  305.  
  306. ;----- BIOS break handler -----------------------------------------
  307. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  308. ; Simply notes that a break was hit.  Flag is checked during input calls.
  309.  
  310. break_handler    proc
  311.     mov    cs:brkkey.len, 1
  312.     iret
  313. break_handler    endp
  314.  
  315.     page
  316.  
  317. ;------ badcmd -------------------------------------------------------
  318. ; Invalid function request by DOS.
  319. badcmd    proc    near
  320.     mov    ax, 813h        ; return "Error: invalid cmd"
  321.     ret
  322. badcmd    endp
  323.  
  324.  
  325. ;------ nopcmd -------------------------------------------------------
  326. ; Unimplemented or dummy function request by DOS.
  327. nopcmd    proc    near
  328.     xor    ax, ax            ; No error, not busy.
  329.     ret
  330. nopcmd    endp
  331.  
  332. ;------- dos function #4 ----------------------------------------
  333. ; Reads CX characters from the keyboard, places them in buffer at
  334. ; ES:SI.
  335. dosfn4    proc    near
  336.     jcxz    dos4done
  337.     mov    di, si
  338. dos4lp: push    cx
  339.     call    getchar
  340.     pop    cx
  341.     stosb
  342.     loop    dos4lp
  343. dos4done:
  344.     xor    ax, ax            ; No error, not busy.
  345.     ret
  346. dosfn4    endp
  347.  
  348. ;-------- dos function #5: non-destructive input, no wait ------
  349. ; One-character lookahead into the keyboard buffer.
  350. ; If no characters in buffer, return BUSY; otherwise, get value of first
  351. ; character of buffer, stuff into request header, return DONE.
  352. dosfn5    proc    near
  353.     call    peekchar
  354.     jz    dos5_busy
  355.  
  356.     lds    bx,req_ptr
  357.     mov    [bx+0Dh], al
  358.     xor    ax, ax            ; No error, not busy.
  359.     jmp    short dos5_exit
  360. dos5_busy:
  361.     MOV    ax, 200h        ; No error, busy.
  362. dos5_exit:
  363.     ret
  364.  
  365. dosfn5    endp
  366.  
  367. ;-------- dos function #6: input status --------------------------
  368. ; Returns "busy" if no characters waiting to be read.
  369. dosfn6    proc    near
  370.     call    peekchar
  371.     mov    ax, 200h        ; No error, busy.
  372.     jz    dos6_exit
  373.     xor    ax, ax            ; No error, not busy.
  374. dos6_exit:
  375.     ret
  376. dosfn6    endp
  377.  
  378. ;-------- dos function #7: flush input buffer --------------------
  379. ; Clears the IBM keyboard input buffer.  Since it is a circular
  380. ; queue, we can do this without knowing the beginning and end
  381. ; of the buffer; all we need to do is set the tail of the queue
  382. ; equal to the head (as if we had read the entire queue contents).
  383. ; Also resets all the device driver's stuffahead buffers.
  384. dosfn7    proc    near
  385.     xor    ax, ax
  386.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  387.     mov    cprseq.len, ax
  388.     mov    brkkey.len, ax
  389.     mov    xlatseq.len, ax
  390.     mov    idseq.len, ax        ;  CRH 3-9-90
  391.  
  392.     mov    ax, abs40
  393.     mov    es, ax
  394.     mov    ax, es:buffer_head    ; clear queue by making the tail
  395.     mov    es:buffer_tail, ax    ; equal to the head
  396.  
  397.     xor    ax, ax            ; no error, not busy.
  398.     ret
  399. dosfn7    endp
  400.  
  401.     page
  402.     if    takeBIOS
  403. ;--- new_vid_bios -------------------------------------------
  404. ; New_vid_bios simply replaces the write_tty call.
  405. ; All other calls get sent to the old video bios.
  406. ; This gives BIOS ANSI capability.
  407. ; However, it takes away the escape character.
  408. ; If this is not desired, just tell init to not take over the vector.
  409.  
  410. new_vid_bios    proc
  411.     cmp    ah, 14
  412.     jz    nvb_write_tty
  413.     jmp    dword ptr cs:old_vid_bios
  414. nvb_write_tty:
  415.     push    cx
  416.     mov    cl, cs:cur_attrib
  417.     ; If in graphics mode, BL is new color
  418.     call    in_g_mode        ; returns carry set if text mode
  419.     jc    nvb_wt_text
  420.         mov    cs:cur_attrib, bl    ; ja?
  421. nvb_wt_text:
  422.     int    29h            ; write AL
  423.     mov    cs:cur_attrib, cl    ; restore color
  424.     pop    cx
  425.     iret
  426.  
  427. new_vid_bios    endp
  428.     endif
  429.  
  430. ;------ int_29 ----------------------------------------------
  431. ; Int 29 handles DOS quick-access putchar.
  432. ; Last device loaded with attribute bit 4 set gets accessed for
  433. ; single-character writes via int 29h instead of via interrupt.
  434. ; Must preserve all registers.
  435. ; Installed as int 29h by dosfn0 (init).
  436. int_29_buf    db    ?
  437.  
  438. int_29    proc    near
  439.     sti
  440.     push    ds
  441.     push    es
  442.     push_all
  443.     mov    cx, 1
  444.     mov    bx, cs
  445.     mov    es, bx
  446.     mov    ds, bx
  447.     mov    si, offset int_29_buf
  448.     mov    byte ptr [si], al
  449.     call    dosfn8
  450.     pop_all
  451.     pop    es
  452.     pop    ds
  453.     iret
  454. int_29    endp
  455.  
  456.     page
  457. ;------ dosfn8 -------------------------------------------------------
  458. ; Handles writes to the device (with or without verify).
  459. ; Called with
  460. ;  CX     = number of bytes to write
  461. ;  ES:SI = transfer buffer
  462. ;  DS     = CS, so we can access local variables.
  463.  
  464. dosfn8    proc    near
  465.  
  466.     mov    f_cptr_seg, es    ; save segment of char ptr
  467.  
  468.     ; Read the BIOS buffer address/cursor position variables.
  469.     mov    ax, abs40
  470.     mov    ds, ax
  471.     assume    ds:abs40
  472.  
  473.     ; Find current video mode and screen size.
  474.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  475.     mov    cs:video_mode, al
  476.     dec    ah            ; ah = max column
  477.     mov    cs:max_x, ah
  478.  
  479.     ; Find current cursor coordinates.
  480.     mov    al,active_page
  481.     cbw
  482.     add    ax,ax
  483.     xchg    bx,ax
  484.     mov    ax,cursor_posn[bx]
  485.     mov    cs:cur_coords,AX
  486.  
  487.     ; Find video buffer segment address; adjust it
  488.     ; so the offset is zero; return in AX.
  489.  
  490.     ; DS is abs40.
  491.     ; Find 6845 address.
  492.     mov    ax, addr_6845
  493.     mov    cs:port_6845, ax
  494.     ; Find video buffer address.
  495.     MOV    AX,crt_start
  496.     shr    ax, 1
  497.     shr    ax, 1
  498.     shr    ax, 1
  499.     shr    ax, 1
  500.     add    ah, 0B0h        ; assume it's a monochrome card...
  501.     CMP    cs:video_mode,07
  502.     jz    d8_gots
  503.     add    ah, 8            ; but if not mode 7, it's color.
  504. d8_gots:
  505.     push    cs
  506.     pop    ds
  507.     assume    ds:code
  508.     mov    video_seg, ax
  509.     mov    es, ax
  510.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  511.  
  512.     ; | If in graphics mode, clear old pseudocursor
  513.     call    in_g_mode
  514.     jc    d8_no_cp
  515.         call    pseudocursor    ; write block in xor
  516. d8_no_cp:
  517.     mov    bx, xlate_tab_ptr    ; get pointer to translation table
  518.  
  519.     mov    ah, cur_attrib
  520.     mov    ds, f_cptr_seg        ; get segment of char ptr
  521.     assume    ds:nothing
  522.     cld                ; make sure we'll increment
  523.  
  524.     ; The Inner Loop: 4+12 +4+4 +4+4 +0+15 +4+4 +4+18 = 77 cycles/loop
  525.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  526.     ; At that speed, it takes 32 milliseconds to fill a screen.
  527.  
  528.     ; Get a character, put it on the screen, repeat 'til end of line
  529.     ; or no more characters.
  530.     jcxz    f_loopdone        ; if count = 0, we're already done.
  531.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  532.     jnz    f_in_escapex        ; jump to escape sequence handler.
  533.  
  534. f_tloop:; | If in graphics mode, jump to alternate loop
  535.     ; | What a massive kludge!  A better approach would have been
  536.     ; | to collect characters for a "write n chars" routine
  537.     ; | which would handle both text and graphics modes.
  538.     call    in_g_mode
  539.     jc    f_t_cloop
  540.     jmp    f_g_cloop
  541.  
  542. f_t_cloop:
  543.     LODSB                ; get char! (al = ds:[si++])
  544.     cmp    al,0            ; skip nulls CRH 22 Feb 90
  545.     jnz    f_t_cloop1        ;   ditto
  546.     loop    f_t_cloop        ;   ditto
  547.     jmp    short f_loopdone    ;   ditto
  548. f_t_cloop1:
  549.     cmp    al, 28            ; is it a control char?
  550.     jb    f_control        ;  maybe...
  551. f_t_nctl:
  552.     seg_cs
  553.     xlat
  554.     STOSW                ; Put Char! (es:[di++] = ax)
  555.     dec    dx            ; count down to end of line
  556.     loopnz    f_t_cloop        ; and go back for more.
  557.     jz    f_t_at_eol        ; at end of line; maybe do a crlf.
  558.     jmp    short f_loopdone
  559.  
  560. f_looploop:
  561. f_ansi_exit:                ; in case we switched into
  562.     loopnz    f_tloop            ; a graphics mode
  563. f_t_at_eol:
  564.     jz    f_at_eol
  565.  
  566. f_loopdone:
  567.  
  568.     ;--------- All done with write request -----------
  569.     ; DI is cursor address; cursor position in cur_y, dl.
  570.     mov    ax, cs
  571.     mov    ds, ax            ; get our segment back
  572.     assume    ds:code
  573.  
  574.     ; Restore cur_x = max_x - dx + 1.
  575.     mov    al, max_x
  576.     inc    al
  577.     sub    al, dl
  578.     mov    cur_x, al
  579.     ; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
  580.     call    set_pseudocursor
  581.     ; Return to DOS.
  582.     xor    ax, ax            ; No error, not busy.
  583.     ret
  584.  
  585.     ;---- handle control characters ----
  586.     ; Note: cur_x is not kept updated in memory, but can be
  587.     ; computed from max_x and dx.
  588.     ; Cur_y is kept updated in memory.
  589. f_control:
  590.     cmp    al, 27            ; Is it an escape?
  591.     jz    f_escapex
  592.     cmp    al, 13            ; carriage return?
  593.     jz    f_cr
  594.     cmp    al, 10            ; line feed?
  595.     jz    f_lf
  596.     cmp    al, 9            ; tab?
  597.     jz    f_tabx
  598.     cmp    al, 8            ; backspace?
  599.     jz    f_bs
  600.     cmp    al, 7            ; bell?
  601.     jz    f_bell
  602.     jmp    f_nctl            ; then it is not a control char.
  603.  
  604. f_tabx: jmp    f_tab
  605. f_escapex:
  606.     jmp    f_escape
  607. f_in_escapex:
  608.     jmp    f_in_escape
  609.  
  610. f_bs:    ;----- Handle backspace -----------------
  611.     ; Moves cursor back one space without erasing.    No wraparound.
  612.     cmp    dl, cs:max_x        ; wrap around to previous line?
  613.     ja    fbs_wrap        ; yep; disallow it.
  614.     dec    di            ; back up one char & attrib,
  615.     dec    di
  616.     inc    dx            ; and note one more char left on line.
  617. fbs_wrap:
  618.     jmp    f_looploop
  619.  
  620. f_bell: ;----- Handle bell ----------------------
  621.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  622.     call    beep
  623.     or    al, al            ; clear z
  624. ; old (more portable) version:
  625. ;    mov    ax, 0e07h        ; "write bell to tty simulator"
  626. ;    mov    bx, 0            ; "page zero, color black"
  627. ;    int    10h            ; call BIOS
  628. ;    mov    ah, cs:cur_attrib    ; restore current attribute
  629. ;    mov    bx, cs:xlate_tab_ptr    ; restore translate table address
  630. ;    or    al, al            ; al still 7; this clears Z.
  631.     jmp    f_looploop        ; Let main loop decrement cx.
  632.  
  633. f_cr:    ;----- Handle carriage return -----------
  634.     ; di -= cur_x<<1;        set di= address of start of line
  635.     ; dx=max_x+1;            set bx= chars left in line
  636.     mov    al, cs:max_x
  637.     inc    al
  638.     sub    al, dl            ; Get cur_x into ax.
  639.     mov    ah, 0
  640.     sub    di, ax
  641.     sub    di, ax
  642.     mov    dl, cs:max_x        ; Full line ahead of us.
  643.     inc    dx
  644.     mov    ah, cs:cur_attrib    ; restore current attribute
  645.     or    al, 1            ; clear z
  646.     jmp    f_looploop        ; and let main loop decrement cx
  647.  
  648. f_at_eol:
  649.     ;----- Handle overrunning right end of screen -------
  650.     ; cx++;                compensate for double loop
  651.     ; if (!wrap_flag) { dx++; di-=2; }
  652.     ; else do_crlf;
  653.     inc    cx
  654.     test    cs:wrap_flag, 1
  655.     jnz    feol_wrap
  656.         dec    di
  657.         dec    di
  658.         inc    dx
  659.         jmp    f_looploop
  660. feol_wrap:
  661.     ; dx=max_x+1;            set bx= chars left in line
  662.     ; di -= 2*(max_x+1);
  663.     ; do_lf
  664.     mov    dl, cs:max_x
  665.     inc    dx
  666.     sub    di, dx
  667.     sub    di, dx
  668.     ; fall thru to line feed routine
  669.  
  670. f_lf:    ;----- Handle line feed -----------------
  671.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  672.     ; else { cur_y++; di += max_x<<1;    else increment Y
  673.  
  674.     mov    al, cs:max_y
  675.     cmp    cs:cur_y, al
  676.     jb    flf_noscroll
  677.         call    scroll_up        ; preserves bx,cx,dx,si,di
  678.         jmp    short flf_done
  679. flf_noscroll:
  680.     inc    cs:cur_y
  681.     mov    al, cs:max_x
  682.     mov    ah, 0
  683.     inc    ax
  684.     add    ax, ax
  685.     add    di, ax
  686. flf_done:
  687.     mov    ah, cs:cur_attrib        ; restore current attribute
  688.     or    al, 1            ; clear z
  689.     jmp    f_looploop        ; and let main loop decrement cx
  690.  
  691. f_tab:    ;----- Handle tab expansion -------------
  692.     ; Get cur_x into al.
  693.     mov    al, cs:max_x
  694.     inc    al
  695.     sub    al, dl
  696.     ; Calculate number of spaces to output.
  697.     push    cx            ; save cx
  698.     mov    ch, 0
  699.     mov    cl, al            ; get zero based x coordinate
  700.     and    cl, 7
  701.     neg    cl
  702.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  703.     sub    dx, cx            ; update chars-to-eol, maybe set z
  704.     pushf                ; || save Z for main loop
  705.     ; ah is still current attribute.  Move CX spaces to the screen.
  706. ;
  707. ; mods to create version with non destructive tab. CRH 23 FEB 90
  708. ;    mov    al, '='         ;xxxx
  709.     call    in_g_mode        ; | graphics mode
  710.     jnc    f_tab_putc        ; |
  711. ;    REP    STOSW             ;xxx
  712. ntl:                    ; added for non-destructive TAB
  713.     inc    di            ; added for non-destructive TAB
  714.     inc    di            ; added for non-destructive TAB
  715.     loop    ntl            ; added for non-destructive TAB
  716.     popf                ; || restore Z flag for main loop test
  717.     pop    cx            ; restore cx
  718.     jmp    f_looploop        ; Let main loop decrement cx.
  719.  
  720. ;--------------- graphics mode support -----------------------
  721.  
  722. f_tab_putc:    ; graphics mode- call putc to put the char
  723.     add    dx, cx            ; move back to start of tab
  724. f_tp_lp:
  725. ;
  726. ; mod to create version with non destructive tab. CRH 23 FEB 90
  727. ;    call    putchar
  728.     dec    dx            ; go to next cursor position
  729.     loop    f_tp_lp
  730.     popf                ; Z set if wrapped around EOL
  731.     pop    cx
  732.     jmp    f_looploop
  733.  
  734. ;---- in_g_mode -------------
  735. ; Returns Carry set if not in a graphics mode.
  736. ; Preserves all registers.
  737.  
  738. in_g_mode    proc    near
  739.     cmp    cs:video_mode, 4
  740.     jb    igm_stc
  741.     cmp    cs:video_mode, 7
  742.     jz    igm_stc
  743.     clc
  744.     ret
  745. igm_stc:
  746.     stc
  747.     ret
  748. in_g_mode    endp
  749.  
  750. ;---- Where to go when a character turns out not to be special
  751. f_nctl:
  752. f_not_ansi:
  753.     call    in_g_mode
  754.     jnc    f_g_nctl        ; graphics mode
  755. f_jmptnctl:
  756.     jmp    f_t_nctl        ; text mode
  757.  
  758. ;---- Alternate main loop for graphics mode ----
  759. f_g_cloop:
  760.     LODSB                ; get char! (al = ds:[si++])
  761.     cmp    al,0            ; skip nulls CRH 22 Feb 90
  762.     jnz    f_g_cloop1        ;   ditto
  763.     loop    f_g_cloop        ;   ditto
  764.     jmp    f_loopdone        ;   ditto
  765. f_g_cloop1:
  766.     cmp    al, 28            ; is it a control char?
  767.     jb    f_g_control        ;  maybe...
  768. f_g_nctl:
  769.     seg_cs
  770.     xlat
  771.     call    putchar
  772.     dec    dx            ; count down to end of line
  773.     loopnz    f_g_cloop        ; and go back for more.
  774.     jz    f_g_at_eol        ; at end of line; maybe do a crlf.
  775.     jmp    f_loopdone
  776.  
  777. f_g_control:    jmp    f_control
  778. f_g_at_eol:    jmp    f_at_eol
  779.  
  780. ;---- putchar ------------------------------------------------
  781. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  782. ; On entry, registers set up as per xy_to_regs.
  783. ; Preserves all registers.
  784. putchar proc    near
  785.     push    dx
  786.     push    cx
  787.     push    bx
  788.     push    ax
  789.     ; 1. Set cursor position.
  790.     mov    al, cs:max_x
  791.     inc    al
  792.     sub    al, dl
  793.     mov    cs:cur_x, al
  794.     mov    dx, cs:cur_coords    ; get X & Y into DX
  795.     xor    bx, bx            ; choose dpy page 0
  796.     mov    ah, 2            ; chose "Set Cursor Position"
  797.     int    10h            ; call ROM BIOS
  798.     ; 2. Write char & attribute.
  799.     mov    cx, 1
  800.     pop    ax            ; get char in AL
  801.     push    ax
  802.     mov    bl, ah            ; attribute in BL
  803.     mov    bh, 0
  804.     mov    ah, 9
  805.     int    10h
  806.     pop    ax
  807.     pop    bx
  808.     pop    cx
  809.     pop    dx
  810.     ret
  811. putchar endp
  812.  
  813. ;---- set_pseudocursor ------------
  814. ; If in graphics mode, set pseudocursor, else set real cursor.
  815. ; Destroys DS!!!!
  816.  
  817. set_pseudocursor    proc    near
  818.     call    in_g_mode
  819.     jnc    pseudocursor
  820. ; old (more portable, but slower) version
  821. ;    mov    dx, cur_coords        ; get X & Y into DX
  822. ;    xor    bx, bx            ; choose dpy page 0
  823. ;    mov    ah, 2            ; chose "Set Cursor Position"
  824. ;    int    10h            ; call ROM BIOS
  825.  
  826.     ; Write directly to 6845 cursor address register.
  827.     mov    bx, di
  828.     shr    bx, 1            ; convert word index to byte index
  829.  
  830.     mov    dx, port_6845
  831.     mov    al, 0eh
  832.     out    dx, al
  833.  
  834.     jmp    $+2
  835.     inc    dx
  836.     mov    al, bh
  837.     out    dx, al
  838.  
  839.     jmp    $+2
  840.     dec    dx
  841.     mov    al, 0fh
  842.     out    dx, al
  843.  
  844.     jmp    $+2
  845.     inc    dx
  846.     mov    al, bl
  847.     out    dx, al
  848.  
  849.     ; Set cursor position in low memory.
  850.     assume    ds:abs40
  851.     mov    ax, abs40
  852.     mov    ds, ax
  853. ; Does anybody ever use anything but page zero?
  854. ;    mov    al,active_page
  855. ;    cbw
  856. ;    add    ax,ax
  857. ;    xchg    bx,ax
  858.     mov    ax, cs:cur_coords
  859.     mov    cursor_posn,ax
  860.     ret
  861.  
  862.     assume    ds:code
  863. set_pseudocursor    endp
  864.  
  865.  
  866. ;---- pseudocursor --------------------------------------------------
  867. ; Writes a color 15 block in XOR at the current cursor location.
  868. ; Preserves DS, ES, BX, CX, DX, SI, DI.
  869. ; Should be disableable- the pseudocursor slows down single-char
  870. ; writes by a factor of three.
  871. pseudocursor    proc    near
  872.     mov    ax, 8f16h    ; xor, color 15, ^V (small block)
  873.     call    putchar
  874.     ret
  875. pseudocursor    endp
  876.  
  877. ;--------------- end of graphics mode support --------------------
  878.  
  879. dosfn8    endp
  880.  
  881. ;--- get_blank_attrib ------------------------------------------------
  882. ; Determine new attribute and character for a new blank region.
  883. ; Use current attribute, just disallow blink and underline.
  884. ; (Pretty strange way to do it.  Might want to disallow rev vid, too.)
  885. ; Returns result in AH, preserves all other registers.
  886. get_blank_attrib    proc    near
  887.     mov    ah, 0
  888.     call    in_g_mode
  889.     jnc    gb_aok            ; if graphics mode, 0 is bkgnd
  890.  
  891.     mov    ah, cs:cur_attrib
  892.     and    ah, 7fh            ; disallow blink
  893.     cmp    cs:video_mode, 7    ; monochrome?
  894.     jnz    gb_aok
  895.         cmp    ah, 1        ; underline?
  896.         jnz    gb_aok
  897.         mov    ah, 7        ; yep- set it to normal.
  898. gb_aok: ret
  899. get_blank_attrib    endp
  900.  
  901.  
  902. ;---- scroll_up ---------------------------------------------------
  903. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  904. ; Moves screen up 1 line, fills the last line with blanks.
  905. ; Attribute of blanks is the current attribute sans blink and underline.
  906.  
  907. scroll_up    proc    near
  908.     push    ax
  909.     push    bx
  910.     push    cx
  911.     push    dx
  912.  
  913.     call    get_blank_attrib
  914.     mov    bh, ah            ; color to use on new blank areas
  915.     mov    al, 1            ; AL is number of lines to scroll.
  916.     mov    ah, 6            ; BIOS: scroll up
  917.     mov    cl, 0            ; upper-left-x of data to scroll
  918.     mov    ch, 0            ; upper-left-y of data to scroll
  919.     mov    dl, cs:max_x        ; lower-rite-x
  920.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  921.     int    10h            ; call BIOS to scroll a rectangle.
  922.  
  923.     pop    dx
  924.     pop    cx
  925.     pop    bx
  926.     pop    ax
  927.     ret
  928. scroll_up    endp
  929.  
  930. ;---- lookup -----------------------------------------------
  931. ; Called by getchar, peekchar, and key to see if a given key has
  932. ; been redefined.
  933. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  934. ; Returns with Z cleared if no redefinition; otherwise,
  935. ; Z is set, SI points to redefinition string, CX is its length.
  936. ; Preseves AL, all but CX and SI.
  937. ; Redefinition table organization:
  938. ;  Strings are stored in reversed order, first char last.
  939. ;  The word following the string is the character to be replaced;
  940. ;  the next word is the length of the string sans header.
  941. ; param_end points to the last byte used by the parameter buffer;
  942. ; redef_end points to the last word used by the redef table.
  943.  
  944. lookup    proc    near
  945.     mov    si, redef_end        ; Start at end of table, move down.
  946.     or    al, al
  947.     jz    lu_lp
  948.     mov    ah, 0            ; clear extraneous scan code
  949. lu_lp:    cmp    si, param_end
  950.     jbe    lu_notfound        ; If below redef table, exit.
  951.     mov    cx, [si]
  952.     cmp    ax, [si-2]        ; are you my mommy?
  953.     jz    lu_gotit
  954.     sub    si, 4
  955.     sub    si, cx            ; point to next header
  956.     jmp    lu_lp
  957. lu_notfound:
  958.     or    si, si            ; clear Z
  959.     jmp    short lu_exit
  960. lu_gotit:
  961.     sub    si, 2
  962.     sub    si, cx            ; point to lowest char in memory
  963.     cmp    al, al            ; set Z
  964. lu_exit:
  965.     ret
  966. lookup    endp
  967.  
  968. ;---- searchbuf --------------------------------------------
  969. ; Called by getchar and peekchar to see if any characters are
  970. ; waiting to be gotten from sources other than BIOS.
  971. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  972. searchbuf    proc    near
  973.     ; Search the stuffahead buffers.
  974. ;    mov    cx, 4            ; number of buffers to check for chars
  975.     mov    cx, 5            ; CRH 3-9-90
  976.     mov    bx, offset fnkey - 4
  977. sbloop: add    bx, 4            ; point to next buffer record
  978.     mov    si, [bx].len
  979.     or    si, si            ; empty?
  980.     loopz    sbloop            ; if so, loop.
  981.     ret
  982. searchbuf    endp
  983.  
  984. ;---- getchar -----------------------------------------------
  985. ; Returns AL = next char.
  986. ; Trashes AX, BX, CX, BP, SI.
  987. getchar proc    near
  988. gc_searchbuf:
  989.     ; See if any chars are waiting in stuffahead buffers.
  990.     call    searchbuf
  991.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  992.     ; A nonempty buffer was found.
  993.     dec    [bx].len
  994.     dec    si
  995.     mov    bp, [bx].adr        ; get pointer to string
  996.     mov    al, byte ptr ds:[bp][si]; get the char
  997.     ; Recognize function key sequences, move them to highest priority
  998.     ; queue.
  999.     sub    si, 1            ; set carry if si=0
  1000.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  1001.     cmp    bx, offset fnkey
  1002.     jz    gc_nofnkey        ; already highest priority -> done.
  1003.     or    al, al
  1004.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  1005.     ; Found a function key; move it to highest priority queue.
  1006.     dec    [bx].len
  1007.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  1008. gc_fnkey:
  1009.     mov    fnkey.len, 1
  1010.     mov    fnkeybuf, ah        ; save it.
  1011. gc_nofnkey:
  1012.     ; Valid char in AL.  Return with it.
  1013.     jmp    short gcdone
  1014.  
  1015. gc_trykbd:
  1016.     ; Actually get a character from the keyboard.
  1017.     mov    ah, 0
  1018.     int    16h            ; BIOS returns with char in AX
  1019.     ; If it's Ctrl-break, it has already been taken care of.
  1020.     or    ax, ax
  1021.     jz    gc_trykbd
  1022.  
  1023.     ; Look in the reassignment table to see if it needs translation.
  1024.     call    lookup            ; Z=found; CX=length; SI=ptr
  1025.     jnz    gc_noredef
  1026.     ; Okay; set up the reassignment, and run thru the translation code.
  1027.     mov    xlatseq.len, cx
  1028.     mov    xlatseq.adr, si
  1029.     jmp    gc_searchbuf
  1030. gc_noredef:
  1031.     ; Is it a function key?
  1032.     cmp    al, 0
  1033.     jz    gc_fnkey        ; yep- special treatment.
  1034. gcdone: ret    ; with character in AL.
  1035.  
  1036. getchar endp
  1037.  
  1038. ;---- peekchar -----------------------------------------------
  1039. ; Returns Z if no character ready, AL=char otherwise.
  1040. ; Trashes AX, BX, CX, BP, SI.
  1041. peekchar    proc    near
  1042. pc_searchbuf:
  1043.     call    searchbuf
  1044.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1045.     ; A nonempty buffer was found.
  1046.     dec    si
  1047.     mov    bp, [bx].adr        ; get pointer to string
  1048.     mov    al, byte ptr ds:[bp][si]; get the char
  1049.     ; Valid char from buffer in AL.  Return with it.
  1050.     jmp    short pcdone
  1051. pc_trykbd:
  1052.     ; Actually peek at the keyboard.
  1053.     mov    ah, 1
  1054.     int    16h            ; BIOS returns with char in AX
  1055.     jz    pcexit
  1056.     ; If it's control-break, it's already been taken care of.
  1057.     or    ax, ax
  1058.     jnz    pc_notbrk
  1059.     mov    ah, 0
  1060.     int    16h            ; so get rid of it!
  1061.     jmp    short pc_trykbd
  1062. pc_notbrk:
  1063.     ; Look in the reassignment table to see if it needs translation.
  1064.     call    lookup            ; Z=found; CX=length; SI=ptr
  1065.     jnz    pcdone            ; Nope; just return the char.
  1066.     ; Okay; get the first code to be returned.
  1067.     add    si, cx
  1068.     mov    al, [si-1]
  1069. pcdone: or    ah, 1            ; NZ; char ready!
  1070. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1071. peekchar    endp
  1072.  
  1073. ;---- beep ------------------------------------------------------
  1074. ; Beep speaker; period given by beep_div, duration by beep_len.
  1075. ; Preserves all registers.
  1076.  
  1077. beep_div    dw    1300        ; fairly close to IBM beep
  1078. beep_len    dw    3        ; 3/18 sec- shorter than IBM
  1079.  
  1080. beep    proc    near
  1081.     push_all
  1082.  
  1083.     mov    al, 10110110b        ; select 8253
  1084.     mov    dx, 43h            ; control port address
  1085.     out    dx, al
  1086.     dec    dx            ; timer 2 address
  1087.     mov    ax, cs:beep_div
  1088.     jmp    $+2
  1089.     out    dx, al            ; low byte of divisor
  1090.     xchg    ah, al
  1091.     jmp    $+2
  1092.     out    dx, al            ; high byte of divisor
  1093.     mov    dx, 61h
  1094.     jmp    $+2
  1095.     in    al, dx            ; get current value of control bits
  1096.     push    ax
  1097.     or    al, 3
  1098.     jmp    $+2
  1099.     out    dx, al            ; turn speaker on
  1100.  
  1101.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1102.     push    es
  1103.     mov    ax, abs40
  1104.     mov    es, ax
  1105.     assume    es:abs40
  1106.     mov    bx, timer_low
  1107.     mov    cx, -1
  1108. beeplp:    mov    ax, timer_low
  1109.     sub    ax, bx
  1110.     cmp    ax, cs:beep_len
  1111.     jg    beepover
  1112.     loop    beeplp
  1113. beepover:
  1114.     pop    es
  1115.     assume    es:code
  1116.  
  1117.     ; Turn off speaker
  1118.     pop    ax
  1119.     and    al, not 3        ; turn speaker off
  1120.     out    dx, al
  1121.     pop_all
  1122.     ret
  1123. beep    endp
  1124.  
  1125. CODE    ends
  1126.  
  1127.     end                ; of nansi.asm
  1128.     page    66, 132
  1129.